﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UserScript.ExpressionParsing;

namespace UserScript.Tests
{
    [TestClass]
    public class CompiledTests
    {
        public class TestClass
        {
            public string FirstName { get; set; }
            public int Integer { get; set; }

            public double Percent { get; set; }

            public object NullValue { get; set; }

            public double Number { get; set; }

            public string reverse(string arg)
            {
                return string.Concat(arg.Reverse());
            }
        }

        [TestMethod]
        public void Constant()
        {
            const string input = "Hello firstname.";

            var compiled = PrecompiledScript<TestClass>.Compile(input);

            var result = compiled.Execute(new TestClass() { FirstName = "Matthew", Integer = 12 });

            Assert.AreEqual("Hello firstname.", result);
        }

        [TestMethod]
        public void StaticAnalysis()
        {
            const string input = "Name: {FirstName}. Name Length: {len(FirstName) + 1}.";

            var method = typeof (ExpressionFunctionExecutor).GetMethod("len");

            var compiled = PrecompiledScript<TestClass>.GetExpressionsWith(input, method).ToList();

            Assert.AreEqual(1, compiled.Count);
        }

        [TestMethod]
        public void CustomFunction()
        {
            const string input = "Name: {reverse(FirstName)}.";

            var compiled = PrecompiledScript<TestClass>.Compile(input);

            var result = compiled.Execute(new TestClass() { FirstName = "Matthew", Integer = 12 });

            Assert.AreEqual("Name: wehttaM.", result);
        }

        [TestMethod]
        public void SingleProperty()
        {
            const string input = "Hello {firstName}.";

            var compiled = PrecompiledScript<TestClass>.Compile(input);

            var result = compiled.Execute(new TestClass() { FirstName = "Matthew", Integer = 12 });

            Assert.AreEqual("Hello Matthew.", result);
        }

        [TestMethod]
        public void SingleFunctionSingleArg()
        {
            const string input = "Length: {len(firstName)}.";

            var compiled = PrecompiledScript<TestClass>.Compile(input);

            var result = compiled.Execute(new TestClass() { FirstName = "Matthew", Integer = 12 });

            Assert.AreEqual("Length: 7.", result);
        }

        [TestMethod]
        public void SingleFunctionSingleLiteralArg()
        {
            const string input = "Length: {len(\"Test\")}";

            var compiled = PrecompiledScript<TestClass>.Compile(input);

            var result = compiled.Execute(new TestClass { FirstName = "Matthew", Integer = 12 });

            Assert.AreEqual("Length: 4", result);
        }

        //[TestMethod]
        //public void SingleFunctionSingleLiteralArgAndUnaryOperator()
        //{
        //    const string input = "Length: {-len(\"Test\")}";

        //    var compiled = PrecompiledScript<TestClass>.Compile(input);

        //    var result = compiled.Execute(new TestClass { FirstName = "Matthew", Integer = 12 });

        //    Assert.AreEqual("Length: -4", result);
        //}

        [TestMethod]
        public void CueWithNumber()
        {
            var compiled = PrecompiledScript<TestClass>.Compile("Hello there, {format(Number, \"N2\")}.");

            var result = compiled.Execute(new TestClass { FirstName = "Matthew", Number = 12.58 });

            Assert.AreEqual("Hello there, 12.58.", result);
        }

        [TestMethod]
        public void CueWithNumberAndCulture()
        {
            var compiled = PrecompiledScript<TestClass>.Compile("Number: {format(Number, \"N2\", \"fr-FR\")}.");

            var result = compiled.Execute(new TestClass { FirstName = "Matthew", Number = 12.58 });

            Assert.AreEqual("Number: 12,58.", result);
        }

        [TestMethod]
        public void NullValue()
        {
            var compiled = PrecompiledScript<TestClass>.Compile("Hello there, {nullvalue}.");

            var result = compiled.Execute(new TestClass { FirstName = "Matthew", Integer = 12 });

            Assert.AreEqual("Hello there, .", result);
        }

        [TestMethod]
        public void NullLiteral()
        {
            try
            {
                var compiled = PrecompiledScript<TestClass>.Compile("Hello there, {len(null)}.");

                var result = compiled.Execute(new TestClass { FirstName = "Matthew", Integer = 12 });

                Assert.Fail();
            }
            catch
            {
                // success
            }
        }

        [TestMethod]
        public void CueWithPercent()
        {
            var compiled = PrecompiledScript<TestClass>.Compile("Hello there, {firstname}. Your number is {format(Number, \"N2\")}. Percent: {padleft(format(percent, \"P2\"), 10)}.");

            var result = compiled.Execute(new TestClass { FirstName = "Matthew", Number = 12.579, Percent = 0.0157 });

            Assert.AreEqual("Hello there, Matthew. Your number is 12.58. Percent:     1.57 %.", result);
        }


        [TestMethod]
        public void CueWithIifTrueAndComplexExpression()
        {
            var compiled = PrecompiledScript<TestClass>.Compile("Hello{if(len(firstname) > 0, \" \" + firstname + \". Your number is \" + format(number, \"N2\") + \"? Percent: \" + padleft(format(percent, \"P2\"), 10), \". You are not logged in\")}.");

            var result = compiled.Execute(new TestClass { FirstName = "Matthew", Number = 12.58, Percent = 0.0157 });

            Assert.AreEqual("Hello Matthew. Your number is 12.58? Percent:     1.57 %.", result);
        }
        [TestMethod]
        public void CueWithIifFalse()
        {
            string input = "Hello {if(isblank(firstname), \"no name\", firstname)},";

            var compiled = PrecompiledScript<TestClass>.Compile(input);

            var result = compiled.Execute(new TestClass { FirstName = "", Number = 12.58, Percent = 0.0157 });

            Assert.AreEqual("Hello no name,", result);
        }


        [TestMethod]
        public void CueWithIifFalseAndComplexExpression()
        {
            var compiled = PrecompiledScript<TestClass>.Compile("Hello{if(len(firstname) > 0, firstname + \". Your number is \" + format(number, \"N2\") + \". Percent: \" + padleft(format(percent, \"P2\"), 10), \". You are not logged in\")}.");

            var result = compiled.Execute(new TestClass { FirstName = "", Integer = 12, Percent = 0.0157 });

            Assert.AreEqual("Hello. You are not logged in.", result);
        }
    }
}
